module cpld_uart_receiver (
input            clk,
input            rst_n,

input   [7:0]   lcr,
input           luart_sol_sw,
input            rfifo_rden,
input            serial_in,
input            enable,
input            rx_reset,
input       lsr_read_rise,
input       receiver_wen,
input   [8-1:0]   receiver_wdata,

output   reg [9:0]         counter_t,
output   [5-1:0]   rf_count,
output   [11-1:0]   rf_data_out,
output            error_bit,
output            rf_overrun,
output reg         rx_fifo_wen,
output reg [3:0]       rstate,
output             rfifo_full,
output             rfifo_empty
//output             receiver_wen_pulse;
);
reg   [3:0]   rstate_nxt;
reg   [3:0]   rcounter16;
reg   [2:0]   rbit_counter;
reg   [7:0]   rshift;         // receiver shift register
reg      rparity;      // received parity
reg      rparity_error;
reg      rframing_error;      // framing error flag
reg      rbit_in;
reg      rparity_xor;
reg   [7:0]   counter_b;   // counts the 0 (low) signals
reg   receiver_wen_q;

// RX FIFO signals
//output   [11-1:0]   rf_data_out;
//wire      receiver_wen_pulse;
//wire            rfifo_rden;
//wire            error_bit; // an error (parity or framing) is inside the fifo
wire             break_error = (counter_b == 0);
//wire rfifo_d1_empty;
//wire rx_fifo_full;
//wire asyn_ctrl_fifo_ren;
//asyn_ctrl_d1 asyn_ctrl_fifo(
//			
//			.wr_clk(clk),
//			.wr_reset_n(rst_n ),
//			.wr_en(receiver_wen),
//			.wr_full(rfifo_full),
//			
//			.rd_clk(clk),
//			.rd_reset_n(rst_n ),
//			.rd_en(asyn_ctrl_fifo_ren),
//			.rd_empty(rfifo_d1_empty)
//);
reg [10:0] rfifo_wdata;
reg [10:0] serial_data_in;
reg        serial_data_vld;
wire       serial_data_vld_pulse;

//reg[7:0] async_buffer;
//always @(posedge clk,negedge rst_n) begin
//   if(!rst_n) async_buffer <= 8'h0;
//   else if(receiver_wen) begin
//      async_buffer <= receiver_wdata;
//   end
//end

//always @(posedge clk,negedge rst_n) begin
//   if(!rst_n) rfifo_wdata <= 11'h00;
//   else
always@(*) begin
   if(luart_sol_sw) begin
      rfifo_wdata = {3'b0,receiver_wdata};
   end
   else begin 
      rfifo_wdata = serial_data_in;
   end
end
//
//assign asyn_ctrl_fifo_ren = !rfifo_d1_empty && !rx_fifo_full;
//always @(posedge clk,negedge rst_n) begin
//   if(!rst_n)     rx_fifo_wen <= 1'b0;
always @(*) begin
   if(luart_sol_sw) rx_fifo_wen = receiver_wen;
   else rx_fifo_wen = serial_data_vld_pulse;
end

uart_rfifo #(11) rx_fifo(
   .clk(      clk      ), 
   .rst_n(   rst_n   ),
   .data_in(   rfifo_wdata   ),
   .data_out(   rf_data_out   ),
   .wen(rx_fifo_wen ),
   .ren(      rfifo_rden      ),
   .overrun(   rf_overrun   ),
   .count(      rf_count   ),
   .error_bit (error_bit),
   .fifo_reset(   rx_reset   ),
   .full (rfifo_full),
   .empty(rfifo_empty),
   .reset_status(lsr_read_rise)
);
//);
//
//reg fifo_rx_waddr_msb;
//reg fifo_rx_raddr_msb;
//reg [4:0] rfifo_waddr_ff1,rfifo_waddr_ff2;
//always @(posedge clk,negedge rst_n) begin
//   if(!rst_n) begin
//      fifo_rx_waddr_msb <=0;
//   end
//   else if(serial_data_vld && !rfifo_full&& fifo_rx_waddr==15) fifo_rx_waddr_msb <= !fifo_rx_waddr_msb;
//end
//always @(posedge clk,negedge rst_n) begin
//   if(!rst_n) begin
//      rfifo_waddr_ff1 <=0;
//      rfifo_waddr_ff2 <=0;
//   end
//   else begin
//      rfifo_waddr_ff1 <= {fifo_rx_waddr_msb,fifo_rx_waddr};
//      rfifo_waddr_ff2 <= rfifo_waddr_ff1;
//   end
//end
//
//always @(posedge clk,negedge rst_n) begin
//   if(!rst_n) begin
//      fifo_rx_raddr_msb <=0;
//   end
//   else if(rfifo_rden && !rfifo_empty && fifo_rx_raddr==15) fifo_rx_raddr_msb <= !fifo_rx_raddr_msb;
//end
//
//always @(posedge clk,negedge rst_n) begin
//   if(!rst_n) begin
//      rf_count<=0;
//   end
//   else begin
//      rf_count<= {fifo_rx_raddr_msb^rfifo_waddr_ff2[4],rfifo_waddr_ff2[3:0]}- fifo_rx_raddr[3:0];
//   end
//end


parameter  RX_IDLE           = 4'd0;
parameter  RX_START          = 4'd1;
parameter  RX_BIT            = 4'd2;
parameter  RX_PARITY         = 4'd3;
parameter  RX_STOP           = 4'd4;
parameter  CHECK_PARITY      = 4'd5;
parameter  RX_PREPARE        = 4'd6;
parameter  RX_END_BIT        = 4'd7;
parameter  RX_CALC_PARITY    = 4'd8;
parameter  RX_WAIT           = 4'd9;
parameter  RX_WRITE          = 4'd10;

always @(posedge clk or negedge rst_n) begin
   if (!rst_n) begin
     rstate          <=  RX_IDLE;
   end
   else if(enable) rstate <= rstate_nxt;
end


reg rxd_reg;
reg rxd_reg_d1;
reg neg_rxd;

always @(posedge clk or negedge rst_n) begin
   if(!rst_n) begin
      rxd_reg <=   1'b0;
      rxd_reg_d1 <= 1'b0;
   end
   else begin
      rxd_reg <=   serial_in;
      rxd_reg_d1 <= rxd_reg;
   end
end

always @(posedge clk or negedge rst_n) begin
   if(!rst_n) 
     neg_rxd <= 1'b0;
   else if (rxd_reg == 1'b0 && rxd_reg_d1 == 1'b1)
     neg_rxd <= 1'b1;
   else if (enable)
     neg_rxd <= 1'b0; 
   else
     neg_rxd <= neg_rxd;  
end


always @(*) begin
   case (rstate)
      RX_IDLE : begin
         if (neg_rxd && ~break_error && luart_sol_sw==1'b0)  begin // detected a pulse (start bit?)
            rstate_nxt  =  RX_START;
         end
         else rstate_nxt = rstate;
      end
      RX_START : begin
         if (rcounter16 == 4'd7)    // check the pulse
            if (serial_in==1'b1)   // no start bit
               rstate_nxt =  RX_IDLE;
            else            // start bit detected
               rstate_nxt =  RX_PREPARE;
         else rstate_nxt = rstate;
      end
      RX_PREPARE:begin
         if (rcounter16 == 4'd0) begin
            rstate_nxt      =  RX_BIT;
         end
         else begin
            rstate_nxt =  rstate;
         end
      end
      RX_BIT :   begin
         if (rcounter16 == 4'd0) begin
            rstate_nxt =  RX_END_BIT;
         end
         else rstate_nxt = rstate;
      end
      RX_END_BIT :   begin
         if (rbit_counter==3'b0) begin// no more bits in word
            if (lcr[3]) // choose state based on parity
               rstate_nxt =  RX_PARITY;
            else begin
               rstate_nxt =  RX_STOP;
            end
         end
         else begin   // else we have more bits to read
            rstate_nxt =  RX_BIT;
         end
      end
      RX_PARITY: begin
         if (rcounter16 == 4'd7)  begin // read the parity
            rstate_nxt =  RX_CALC_PARITY;
         end
         else  rstate_nxt = rstate;
      end
      RX_CALC_PARITY : begin    // rcounter equals 6
         rstate_nxt      =  CHECK_PARITY;
      end
      CHECK_PARITY: begin     // rcounter equals 5
         rstate_nxt =  RX_WAIT;
      end
      RX_WAIT : begin
         if (rcounter16 == 4'd0) begin
            rstate_nxt =  RX_STOP;
         end
         else  rstate_nxt = rstate;
      end
      RX_STOP : begin
         if(rcounter16 == 4'd7) begin
            rstate_nxt =  RX_WRITE;
         end
         else  rstate_nxt = rstate;
      end
      RX_WRITE : begin
         if(serial_in | break_error) begin
            rstate_nxt        =  RX_IDLE;
         end
         else if(~rframing_error)  begin
            rstate_nxt         =  RX_START;
         end
         else  rstate_nxt = rstate;
      end
      default : rstate_nxt =  RX_IDLE;
   endcase
end // always of receiver

always @(posedge clk or negedge rst_n) begin
   if (!rst_n)
   begin
      rbit_in             <=  1'b0;
      rcounter16          <=  0;
      rbit_counter       <=  0;
      rparity_xor       <=  1'b0;
      rframing_error    <=  1'b0;
      rparity_error       <=  1'b0;
      rparity             <=  1'b0;
      rshift             <=  0;
      serial_data_vld             <=  1'b0;
      serial_data_in          <=  0;
   end
   else if (enable) begin
      case (rstate)
      RX_IDLE : begin
         serial_data_vld       <=  1'b0;
         serial_data_in      <=  0;
         rcounter16      <=  4'b1110;
      end
      RX_START :   begin
         serial_data_vld            <=  1'b0;
         rcounter16 <=  rcounter16 - 1'b1;
      end
      RX_PREPARE:begin
         case (lcr[1:0])  // number of bits in a word
               2'b00 : rbit_counter <=  3'b100;
               2'b01 : rbit_counter <=  3'b101;
               2'b10 : rbit_counter <=  3'b110;
               2'b11 : rbit_counter <=  3'b111;
         endcase
         if (rcounter16 == 4'd0) begin
            rcounter16   <=  4'b1110;
            rshift      <=  0;
         end
         rcounter16 <=  rcounter16 - 1'b1;
      end
      RX_BIT :   begin
         if(rcounter16 == 4'd7)  begin // read the bit
            case (lcr[1:0])  // number of bits in a word
               2'b00 : rshift[4:0]  <=  {serial_in, rshift[4:1]};
               2'b01 : rshift[5:0]  <=  {serial_in, rshift[5:1]};
               2'b10 : rshift[6:0]  <=  {serial_in, rshift[6:1]};
               2'b11 : rshift[7:0]  <=  {serial_in, rshift[7:1]};
            endcase
         end
         rcounter16 <=  rcounter16 - 1'b1;
      end
      RX_END_BIT :   begin
         if (rbit_counter==3'b0) begin// no more bits in word
            if (!lcr[3]) begin// choose state based on parity
               rparity_error <=  1'b0;  // no parity - no error :)
            end
         end
         else begin     // else we have more bits to read
            rbit_counter <=  rbit_counter - 1'b1;
         end
         rcounter16 <=  4'b1110;
      end
      RX_PARITY: begin
               if (rcounter16 == 4'd7)   // read the parity
               begin
                  rparity <=  serial_in;
               end
               rcounter16 <=  rcounter16 - 1'b1;
            end
      RX_CALC_PARITY : begin    // rcounter equals 6
               rcounter16  <=  rcounter16 - 1'b1;
               rparity_xor <=  ^{rshift,rparity}; // calculate parity on all incoming data
              end
      CHECK_PARITY: begin     // rcounter equals 5
         case ({lcr[5:4]})
            2'b00: rparity_error <=   rparity_xor == 0;  // no error if parity 1
            2'b01: rparity_error <=   rparity_xor == 1;   // error if parity is odd
            2'b10: rparity_error <=  ~rparity;      // parity should sticked to 1
            2'b11: rparity_error <=   rparity;     // parity should be sticked to 0
         endcase
         rcounter16 <=  rcounter16 - 1'b1;
      end
      RX_WAIT :   
         if (rcounter16 == 4'd0) begin
            rcounter16 <=  4'b1110;
         end
         else
            rcounter16 <=  rcounter16 - 1'b1;
      RX_STOP :begin
         if(rcounter16 == 4'd7) begin
            rframing_error <=  !serial_in; // no framing error if input is 1 (stop bit)
         end
         rcounter16 <=  rcounter16 - 1'b1;
      end
      RX_WRITE :   begin
         if(serial_in | break_error) begin
            if(break_error)
               serial_data_in    <=  {3'b100,8'b0}; // break input (empty character) to receiver FIFO
            else begin
               serial_data_in  <=  { 1'b0, rparity_error, rframing_error,rshift};
            end
            serial_data_vld         <=  1'b1;
         end
         else if(~rframing_error)  begin
            serial_data_in  <=  { 1'b0, rparity_error, rframing_error,rshift};
            serial_data_vld         <=  1'b1;
            rcounter16      <=  4'b1110;
         end
      end
      default : begin
         rbit_in            <=  1'b0;
         rcounter16         <=  0;
         rbit_counter       <=  0;
         rparity_xor        <=  1'b0;
         rframing_error     <=  1'b0;
         rparity_error      <=  1'b0;
         rparity            <=  1'b0;
         rshift             <=  0;
         serial_data_vld          <=  1'b0;
         serial_data_in         <=  0;
      end
   endcase
  end  // if (enable)
end // always of receiver

reg serial_data_vld_q;
always @ (posedge clk or negedge rst_n)
begin
  if(!rst_n)
    serial_data_vld_q <= 0;
  else
    serial_data_vld_q <=  serial_data_vld;
end

assign serial_data_vld_pulse = serial_data_vld & ~serial_data_vld_q;



// Break condition detection.
// Works in conjuction with the receiver state machine

reg    [9:0]   toc_value; // value to be set to timeout counter

always @(lcr) begin 
   case (lcr[3:0])
      4'b0000                              : toc_value = 447; // 7 bits
      4'b0100                              : toc_value = 479; // 7.5 bits
      4'b0001,   4'b1000                     : toc_value = 511; // 8 bits
      4'b1100                              : toc_value = 543; // 8.5 bits
      4'b0010, 4'b0101, 4'b1001            : toc_value = 575; // 9 bits
      4'b0011, 4'b0110, 4'b1010, 4'b1101   : toc_value = 639; // 10 bits
      4'b0111, 4'b1011, 4'b1110            : toc_value = 703; // 11 bits
      4'b1111                              : toc_value = 767; // 12 bits
   endcase // case(lcr[3:0])
end

wire [7:0]    brc_value; // value to be set to break counter
assign       brc_value = toc_value[9:2]; // the same as timeout but 1 insead of 4 character times

always @(posedge clk or negedge rst_n)
begin
   if (!rst_n)
      counter_b <=  8'd159;
   else
   if (serial_in)
      counter_b <=  brc_value; // character time length - 1
   else
   if(enable & counter_b != 8'b0)            // only work on enable times  break not reached.
      counter_b <=  counter_b - 1;  // decrement break counter
end // always of break condition detection

///
/// Timeout condition detection
//reg   [9:0]   counter_t;   // counts the timeout condition clocks

always @(posedge clk or negedge rst_n)
begin
   if (!rst_n)
      counter_t <=  10'd639; // 10 bits for the default 8N1
   else
      if(serial_data_vld || rfifo_rden || rf_count == 0) // counter is reset when RX FIFO is empty, accessed or above trigger level
         counter_t <=  toc_value;
      else if (enable && counter_t != 10'b0)  // we don't want to underflow
         counter_t <=  counter_t - 1;      
end
   
endmodule
